//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-07-21     ZhangYihua   first version
//
// Description  : for one AXI-lite master to access several AXI-lite slaves.
//################################################################################

module axi_lite_split #(
parameter           ADDR_BW                 = 16,               // lower address
parameter           DATA_BW                 = 32,
parameter           SLV_REG                 = 1'b0,             // source reg slicer, 1'b1:add slice reg; 1'b0:no slice reg
parameter           MST_NUM                 = 2,
parameter           MST_REG                 = {MST_NUM{1'b1}},  // each bit for a destination reg slicer, 1'b1:add slice reg; 1'b0:no slice reg
parameter           DFT_SLV_EN              = 1'b1              // 1'b1:enable default slave for not-hit address; 1'b0:disable it for full address decoder
) ( 
input                                       rst_n,
input                                       clk,

// higher address decoder
input               [MST_NUM-1:0]           slv_awhit,     // one-hot combinational logic of slv_awaddr
input               [MST_NUM-1:0]           slv_arhit,     // one-hot combinational logic of slv_araddr

// one AXI-lite slave(from source master)
input               [ADDR_BW-1:0]           slv_awaddr,
input                                       slv_awvalid,
output                                      slv_awready,

input               [DATA_BW-1:0]           slv_wdata,
input                                       slv_wvalid,
output                                      slv_wready,

output              [2-1:0]                 slv_bresp,
output                                      slv_bvalid,
input                                       slv_bready,

input               [ADDR_BW-1:0]           slv_araddr,
input                                       slv_arvalid,
output                                      slv_arready,

output              [DATA_BW-1:0]           slv_rdata,
output              [2-1:0]                 slv_rresp,
output                                      slv_rvalid,
input                                       slv_rready,

// several AXI-lite masters(to destination slaves)
output              [MST_NUM*ADDR_BW-1:0]   mst_awaddr,
output              [MST_NUM-1:0]           mst_awvalid,
input               [MST_NUM-1:0]           mst_awready,

output              [MST_NUM*DATA_BW-1:0]   mst_wdata,
output              [MST_NUM-1:0]           mst_wvalid,
input               [MST_NUM-1:0]           mst_wready,

input               [MST_NUM*2-1:0]         mst_bresp,
input               [MST_NUM-1:0]           mst_bvalid,
output              [MST_NUM-1:0]           mst_bready,

output              [MST_NUM*ADDR_BW-1:0]   mst_araddr,
output              [MST_NUM-1:0]           mst_arvalid,
input               [MST_NUM-1:0]           mst_arready,

input               [MST_NUM*DATA_BW-1:0]   mst_rdata,
input               [MST_NUM*2-1:0]         mst_rresp,
input               [MST_NUM-1:0]           mst_rvalid,
output              [MST_NUM-1:0]           mst_rready
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam          ALL_NUM                 = (DFT_SLV_EN==1'b1) ? MST_NUM+1 : MST_NUM;

wire                [ALL_NUM-1:0]           src_awhit;
wire                [ALL_NUM-1:0]           src_arhit;

wire                [ADDR_BW-1:0]           src_awaddr;
wire                                        src_awvalid;
wire                                        src_awready;

wire                [DATA_BW-1:0]           src_wdata;
wire                                        src_wvalid;
wire                                        src_wready;

wire                [2-1:0]                 src_bresp;
wire                                        src_bvalid;
wire                                        src_bready;

wire                [ADDR_BW-1:0]           src_araddr;
wire                                        src_arvalid;
wire                                        src_arready;

wire                [DATA_BW-1:0]           src_rdata;
wire                [2-1:0]                 src_rresp;
wire                                        src_rvalid;
wire                                        src_rready;

wire                                        aw_w_valid;
reg                                         aw_done;
reg                                         w_done;
reg                                         ar_done;

wire                [ADDR_BW-1:0]           split_awaddr[ALL_NUM-1:0];
wire                [ALL_NUM-1:0]           split_awvalid;
wire                [ALL_NUM-1:0]           split_awready;

wire                [DATA_BW-1:0]           split_wdata[ALL_NUM-1:0];
wire                [ALL_NUM-1:0]           split_wvalid;
wire                [ALL_NUM-1:0]           split_wready;

wire                [ALL_NUM*2-1:0]         split_bresp;
wire                [ALL_NUM-1:0]           split_bvalid;
wire                [ALL_NUM-1:0]           split_bready;

wire                [ADDR_BW-1:0]           split_araddr[ALL_NUM-1:0];
wire                [ALL_NUM-1:0]           split_arvalid;
wire                [ALL_NUM-1:0]           split_arready;

wire                [ALL_NUM*DATA_BW-1:0]   split_rdata;
wire                [ALL_NUM*2-1:0]         split_rresp;
wire                [ALL_NUM-1:0]           split_rvalid;
wire                [ALL_NUM-1:0]           split_rready;

//################################################################################
// main
//################################################################################

axi_slicer #(
        .REG_ON                         ((SLV_REG==1'b1) ? 5'b11111 : 5'b00000),
        .ADDR_BW                        (ADDR_BW+MST_NUM                ),
        .DATA_BW                        (DATA_BW                        )
) u_slv_reg ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

    // one AXI-lite slave(from source master)
        .slv_awaddr                     ({slv_awaddr, slv_awhit}        ),
        .slv_awvalid                    (slv_awvalid                    ),
        .slv_awready                    (slv_awready                    ),

        .slv_wdata                      (slv_wdata                      ),
        .slv_wvalid                     (slv_wvalid                     ),
        .slv_wready                     (slv_wready                     ),

        .slv_bresp                      (slv_bresp                      ),
        .slv_bvalid                     (slv_bvalid                     ),
        .slv_bready                     (slv_bready                     ),

        .slv_araddr                     ({slv_araddr, slv_arhit}        ),
        .slv_arvalid                    (slv_arvalid                    ),
        .slv_arready                    (slv_arready                    ),

        .slv_rdata                      (slv_rdata                      ),
        .slv_rresp                      (slv_rresp                      ),
        .slv_rvalid                     (slv_rvalid                     ),
        .slv_rready                     (slv_rready                     ),

    // one AXI-lite master(to destination slave)
        .mst_awaddr                     ({src_awaddr, src_awhit[0+:MST_NUM]}),
        .mst_awvalid                    (src_awvalid                    ),
        .mst_awready                    (src_awready                    ),

        .mst_wdata                      (src_wdata                      ),
        .mst_wvalid                     (src_wvalid                     ),
        .mst_wready                     (src_wready                     ),

        .mst_bresp                      (src_bresp                      ),
        .mst_bvalid                     (src_bvalid                     ),
        .mst_bready                     (src_bready                     ),

        .mst_araddr                     ({src_araddr, src_arhit[0+:MST_NUM]}),
        .mst_arvalid                    (src_arvalid                    ),
        .mst_arready                    (src_arready                    ),

        .mst_rdata                      (src_rdata                      ),
        .mst_rresp                      (src_rresp                      ),
        .mst_rvalid                     (src_rvalid                     ),
        .mst_rready                     (src_rready                     )
);

// both aw and w valid
assign aw_w_valid = src_awvalid & src_wvalid;
always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        aw_done <=`U_DLY 1'b0;
        w_done <=`U_DLY 1'b0;

        ar_done <=`U_DLY 1'b0;
    end else begin
        if ((split_awready&src_awhit)!={ALL_NUM{1'b0}} && aw_w_valid==1'b1 && aw_done==1'b0)
            aw_done <=`U_DLY 1'b1;
        else if (aw_done==1'b1 && w_done==1'b1)
            aw_done <=`U_DLY 1'b0;
        else
            ;

        if ((split_wready&src_awhit)!={ALL_NUM{1'b0}} && aw_w_valid==1'b1 && w_done==1'b0)
            w_done <=`U_DLY 1'b1;
        else if (aw_done==1'b1 && w_done==1'b1)
            w_done <=`U_DLY 1'b0;
        else
            ;

        if ((split_arready&src_arhit)!={ALL_NUM{1'b0}} && src_arvalid==1'b1 && ar_done==1'b0)
            ar_done <=`U_DLY 1'b1;
        else if (ar_done==1'b1)
            ar_done <=`U_DLY 1'b0;
        else
            ;
    end
end

assign src_awready = aw_done & w_done;
assign src_wready  = aw_done & w_done;

assign src_arready = ar_done;

genvar g0;
generate for (g0=0; g0<MST_NUM; g0=g0+1) begin:G_MST
    assign split_awvalid[g0] = aw_w_valid & src_awhit[g0] & (~aw_done);
    assign split_wvalid[g0]  = aw_w_valid & src_awhit[g0] & (~w_done);

    assign split_awaddr[g0] = src_awaddr;
    assign split_wdata[g0] = src_wdata;

    assign split_araddr[g0] = src_araddr;
    assign split_arvalid[g0] = src_arvalid & src_arhit[g0] & (~ar_done);

    axi_slicer #(
        .REG_ON                         ((MST_REG[g0]==1'b1) ? 5'b11111 : 5'b00000),
        .ADDR_BW                        (ADDR_BW                        ),
        .DATA_BW                        (DATA_BW                        )
    ) u_mst_reg ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

    // one AXI-lite slave(from source master)
        .slv_awaddr                     (split_awaddr[g0]               ),
        .slv_awvalid                    (split_awvalid[g0]              ),
        .slv_awready                    (split_awready[g0]              ),

        .slv_wdata                      (split_wdata[g0]                ),
        .slv_wvalid                     (split_wvalid[g0]               ),
        .slv_wready                     (split_wready[g0]               ),

        .slv_bresp                      (split_bresp[g0*2+:2]           ),
        .slv_bvalid                     (split_bvalid[g0]               ),
        .slv_bready                     (split_bready[g0]               ),

        .slv_araddr                     (split_araddr[g0]               ),
        .slv_arvalid                    (split_arvalid[g0]              ),
        .slv_arready                    (split_arready[g0]              ),

        .slv_rdata                      (split_rdata[g0*DATA_BW+:DATA_BW]),
        .slv_rresp                      (split_rresp[g0*2+:2]           ),
        .slv_rvalid                     (split_rvalid[g0]               ),
        .slv_rready                     (split_rready[g0]               ),

    // one AXI-lite master(to destination slave)
        .mst_awaddr                     (mst_awaddr[g0*ADDR_BW+:ADDR_BW]),
        .mst_awvalid                    (mst_awvalid[g0]                ),
        .mst_awready                    (mst_awready[g0]                ),

        .mst_wdata                      (mst_wdata[g0*DATA_BW+:DATA_BW] ),
        .mst_wvalid                     (mst_wvalid[g0]                 ),
        .mst_wready                     (mst_wready[g0]                 ),

        .mst_bresp                      (mst_bresp[g0*2+:2]             ),
        .mst_bvalid                     (mst_bvalid[g0]                 ),
        .mst_bready                     (mst_bready[g0]                 ),

        .mst_araddr                     (mst_araddr[g0*ADDR_BW+:ADDR_BW]),
        .mst_arvalid                    (mst_arvalid[g0]                ),
        .mst_arready                    (mst_arready[g0]                ),

        .mst_rdata                      (mst_rdata[g0*DATA_BW+:DATA_BW] ),
        .mst_rresp                      (mst_rresp[g0*2+:2]             ),
        .mst_rvalid                     (mst_rvalid[g0]                 ),
        .mst_rready                     (mst_rready[g0]                 )
    );
end endgenerate

n2o #(
        .PNUM                           (ALL_NUM                        ),	// source port number
        .DWID                           (DATA_BW                        ),	// data width of one port
        .SMODE                          ("ONEHOT"                       ) 	// sel is onehot signal
) u_rdata_mux ( 
        .sel                            (split_rvalid                   ),	// support one-hot and all-zero input when SMODE
        .np_dat                         (split_rdata                    ),
        .op_dat                         (src_rdata                      )
);

n2o #(
        .PNUM                           (ALL_NUM                        ),	// source port number
        .DWID                           (2                              ),	// data width of one port
        .SMODE                          ("ONEHOT"                       ) 	// sel is onehot signal
) u_rresp_mux ( 
        .sel                            (split_rvalid                   ),	// support one-hot and all-zero input when SMODE
        .np_dat                         (split_rresp                    ),
        .op_dat                         (src_rresp                      )
);

n2o #(
        .PNUM                           (ALL_NUM                        ),	// source port number
        .DWID                           (2                              ),	// data width of one port
        .SMODE                          ("ONEHOT"                       ) 	// sel is onehot signal
) u_bresp_mux ( 
        .sel                            (split_bvalid                   ),	// support one-hot and all-zero input when SMODE
        .np_dat                         (split_bresp                    ),
        .op_dat                         (src_bresp                      )
);

assign src_rvalid = |split_rvalid;
assign src_bvalid = |split_bvalid;

assign split_rready = {ALL_NUM{src_rready}};
assign split_bready = {ALL_NUM{src_bready}};

generate if (DFT_SLV_EN==1'b1) begin:G_DFT_SLV
    
    assign src_awhit[MST_NUM] = ~(|src_awhit[0+:MST_NUM]);
    assign src_arhit[MST_NUM] = ~(|src_arhit[0+:MST_NUM]);
    
    assign split_awvalid[MST_NUM] = aw_w_valid & src_awhit[MST_NUM] & (~aw_done);
    assign split_wvalid[MST_NUM]  = aw_w_valid & src_awhit[MST_NUM] & (~w_done);

    assign split_awaddr[MST_NUM] = src_awaddr;
    assign split_wdata[MST_NUM] = src_wdata;

    assign split_araddr[MST_NUM] = src_araddr;
    assign split_arvalid[MST_NUM] = src_arvalid & src_arhit[MST_NUM] & (~ar_done);

    axi_dft_slv #(
        .ADDR_BW                        (ADDR_BW                        ),	// lower address
        .DATA_BW                        (DATA_BW                        ),
        .DFT_RDATA                      (32'hdead_beef                  )
    ) u_axi_dft_slv ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .slv_awaddr                     (split_awaddr[MST_NUM]          ),
        .slv_awvalid                    (split_awvalid[MST_NUM]         ),
        .slv_awready                    (split_awready[MST_NUM]         ),

        .slv_wdata                      (split_wdata[MST_NUM]           ),
        .slv_wvalid                     (split_wvalid[MST_NUM]          ),
        .slv_wready                     (split_wready[MST_NUM]          ),

        .slv_bresp                      (split_bresp[MST_NUM*2+:2]      ),
        .slv_bvalid                     (split_bvalid[MST_NUM]          ),
        .slv_bready                     (split_bready[MST_NUM]          ),

        .slv_araddr                     (split_araddr[MST_NUM]          ),
        .slv_arvalid                    (split_arvalid[MST_NUM]         ),
        .slv_arready                    (split_arready[MST_NUM]         ),

        .slv_rdata                      (split_rdata[MST_NUM*DATA_BW+:DATA_BW]),
        .slv_rresp                      (split_rresp[MST_NUM*2+:2]      ),
        .slv_rvalid                     (split_rvalid[MST_NUM]          ),
        .slv_rready                     (split_rready[MST_NUM]          )
    );

end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
